#include "ini_config_manager.h"
#include "doubao_semaphore.h"
#include "log_manager.h"

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <sys/stat.h>
#include <cstring>
#include <cerrno>
#include <thread>
#include <string>
#include <sstream>
#include <iomanip>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
// #include <semaphore.h>
// #include <pthread.h>

#define INI_VAR_WORKDIR "workdir"
#define INI_VAR_PROJDIR "projdir"
#define INI_VAR_SRCDIRS "srcdirs"
#define INI_VAR_JOBSCNT "thread_jobs"
#define INI_VAR_SRCTYPES "srctypes"
#define INI_VAR_CTAGSCMD "ctags_cmd"

static std::string g_arg_workdir;
static std::string g_configFile = "config.ini";
static std::string g_config_group_name = "vimkernel_project_config";

static std::string g_workdir;       // 启动vim时所在的路径
static std::string g_projdir;       // vim_project 目录，存放工程相关文件
static std::string g_srcdirs;       // 源代码目录
static std::string g_jobscnt = "4"; // 线程数量

static LogManager logger("/tmp/vim_project.log", 10 * 1024 * 1024);

// ctags前置命令
// 完整命令为: "ctags --c++-kinds=+px --fields=+iaS --extra=+fq --excmd=p -f outputfile -L inputfilelist.txt"
// 末尾的输入输出文件参数在配置文件中无需填写，配置文件只填写前面部分，以便用户修改前置参数
static std::string g_ctagscmd = "ctags --c++-kinds=+px --fields=+iaS --extra=+fq --excmd=p ";

// 源文件类型, 用来传递给find命令查找(通配符)，比如 "*.c, *.cpp, *.h"，不同类型之间用逗号分隔
static std::string g_srctypes = "*.S, *.c, *.h, *.lds, *.dts, *.dtsi, Makefile";

static Semaphore g_wait_filenametags(0);
static Semaphore g_wait_ctags(0);
static Semaphore g_wait_cscope(0);

int cleanProjectFilesOnlyObjs(void)
{
    std::string command;

    // 删除工作目录下的 tags 文件
    command = "rm -f " + g_workdir + "/tags";
    logger.log_debug("%s():执行清理命令: %s", __func__, command.c_str());
    if (system(command.c_str()) != 0)
    {
        logger.log_error("清理失败: %s", (g_workdir + "/tags").c_str());
        return 1;
    }

    // 删除 output_ctags_*
    //command = "rm -f " + g_workdir + "/output_ctags_*";
    command = "rm -f " + g_projdir + "/output_ctags_*";
    logger.log_debug("%s():执行清理命令: %s", __func__, command.c_str());
    if (system(command.c_str()) != 0)
    {
        //logger.log_error("清理失败: %s", (g_workdir + "/output_ctags_*").c_str());
        logger.log_error("清理失败: %s", (g_projdir + "/output_ctags_*").c_str());
        return 1;
    }

    // 删除工程目录下的数据文件
    command = "rm -f " + g_projdir + "/output_*";
    logger.log_debug("%s():执行清理命令: %s", __func__, command.c_str());
    if (system(command.c_str()) != 0)
    {
        logger.log_error("清理失败: %s", (g_projdir + "/output_*").c_str());
        return 1;
    }
    command = "rm -f " + g_projdir + "/input_*";
    logger.log_debug("%s():执行清理命令: %s", __func__, command.c_str());
    if (system(command.c_str()) != 0)
    {
        logger.log_error("清理失败: %s", (g_projdir + "/input_*").c_str());
        return 1;
    }
    return 0;
}

int cleanProjectFilesOnlyCfgs(void)
{
    std::string command;

    // 删除工作目录下的配置文件
    command = "rm -f " + g_projdir + "/config.ini";
    logger.log_debug("%s():执行清理命令: %s", __func__, command.c_str());
    if (system(command.c_str()) != 0)
    {
        logger.log_error("%s():清理失败: %s", __func__, g_arg_workdir + "/config.ini");
        return 1;
    }

    // 删除工程目录下的.vim文件
    command = "rm -f " + g_projdir + "/*.vim";
    logger.log_debug("%s():执行清理命令: %s", __func__, command.c_str());
    if (system(command.c_str()) != 0)
    {
        logger.log_error("%s():清理失败: %s", __func__, (g_projdir + "/*.vim").c_str());
        return 1;
    }

    return 0;
}

// 工程清理函数
int cleanProjectFiles()
{
    // 删除 vim_project 目录
    std::string command = "rm -rf " + g_projdir;
    logger.log_debug("%s():执行清理命令: %s", __func__, command.c_str());
    if (system(command.c_str()) != 0)
    {
        logger.log_error("%s():清理失败: %s", __func__, g_projdir.c_str());
        return 1;
    }

    cleanProjectFilesOnlyObjs();
    return 0;
}

void vim_project_semaphore_init(void)
{
    int ret = 0;
    IniConfigManager configManager;
    configManager.setIniFile(g_configFile);
    std::string strJobs = configManager.getConfig(INI_VAR_JOBSCNT, g_config_group_name);
    long jobs = std::strtol(strJobs.c_str(), nullptr, 10);

    // 已经在全局变量位置初始化了 g_wait_filenametags, g_wait_ctags, g_wait_cscope
}

std::string ctags_get_adapted_cmdprefix(void)
{
    /*
        in shell command:

            1) 过滤出关键字符串: --extra=[+|-]
                ctags --help | grep "\-\-extra=\[+|\-\]"
                echo $?

            2) 过滤出关键字符串: --extras=[+|-]
                ctags --help | grep "\-\-extras=\[+|\-\]"
                echo $?
    */

    std::string cmd_ret = "ctags --c++-kinds=+px --fields=+iaS --excmd=p";
    std::string command;

    // 执行外部命令并获取执行结果(0 or 非0): ctags --help | grep '\-\-extra\=\[+|\-\]' 
    command = "ctags --help | grep '\\-\\-extra\\=\\[+|\\-\\]'";
    if (system(command.c_str()) == 0)
    {
        cmd_ret = "ctags --c++-kinds=+px --fields=+iaS --extra=+fq --excmd=p ";
        return cmd_ret;
    }

    command = "ctags --help | grep '\\-\\-extras\\=\\[+|\\-\\]'";
    if (system(command.c_str()) == 0)
    {
        cmd_ret = "ctags --c++-kinds=+px --fields=+iaS --extras=+fq --excmd=p ";
        return cmd_ret;
    }

    logger.log_warn("你的当前ctags找不到--extra(s)支持,请执行ctags --help查看或者找到合适ctags版本");
    logger.log_warn("本工具匹配规则: 1) --extra=[+|-]    2) --extras=[+|-]");
    logger.log_warn("没有该选项支持会导致OmnicppComplete插件无法正常索引");
    return cmd_ret;
}

void vim_project_config_init(void)
{
    g_configFile = g_projdir + "/config.ini";
    struct stat configInfo;
    if ((stat(g_configFile.c_str(), &configInfo) != 0) || (!(configInfo.st_mode & S_IFREG)))
    {
        logger.log_info("生成默认配置: %s ...", g_configFile.c_str());

        // 如果不存在配置文件，则通过 ini_config_manager 接口创建
        IniConfigManager configManager;
        configManager.setIniFile(g_configFile);

        g_workdir = g_arg_workdir;
        g_projdir = g_workdir + "/vim_project";
        g_srcdirs = g_workdir + ", absdir1, absdir2";


        // ctags --extra(s) 自动适配命令
        g_ctagscmd = ctags_get_adapted_cmdprefix();


        // 新增配置项
        configManager.addConfig(INI_VAR_WORKDIR, g_workdir, g_config_group_name);
        configManager.addConfig(INI_VAR_PROJDIR, g_projdir, g_config_group_name);
        configManager.addConfig(INI_VAR_SRCDIRS, g_srcdirs, g_config_group_name);
        // srcdirs 里默认第一项是workdir, 后面的其他路径是用户可以手动添加的自定义源码路径

        configManager.addConfig(INI_VAR_JOBSCNT, g_jobscnt, g_config_group_name);
        configManager.addConfig(INI_VAR_SRCTYPES, g_srctypes, g_config_group_name);
        configManager.addConfig(INI_VAR_CTAGSCMD, g_ctagscmd, g_config_group_name);

        logger.log_info("生成默认配置: %s OK", g_configFile.c_str());
    }
    else
    {
        IniConfigManager configManager;
        configManager.setIniFile(g_configFile);

        logger.log_info("载入工程配置: %s ...", g_configFile.c_str());

        // 如果路径更新，则需要更新配置文件里的路径信息
        g_workdir = configManager.getConfig(INI_VAR_WORKDIR, g_config_group_name);
        if (g_workdir != g_arg_workdir)
        {
            g_workdir = g_arg_workdir;
            g_projdir = g_workdir + "/vim_project";
            configManager.addConfig(INI_VAR_WORKDIR, g_workdir, g_config_group_name);
            configManager.addConfig(INI_VAR_PROJDIR, g_projdir, g_config_group_name);

            // 把 srcdirs 的第一项，并替换成 g_workdir
            g_srcdirs = configManager.getConfig(INI_VAR_SRCDIRS, g_config_group_name);
            std::string new_srcdirs = g_workdir + "," + g_srcdirs.substr(g_srcdirs.find(",") + 1);
            configManager.addConfig(INI_VAR_SRCDIRS, new_srcdirs, g_config_group_name);
            logger.log_info("路径配置自动更新为: %s", g_workdir.c_str());
        }

        // 读取配置项
        g_srcdirs = configManager.getConfig(INI_VAR_SRCDIRS, g_config_group_name);
        g_jobscnt = configManager.getConfig(INI_VAR_JOBSCNT, g_config_group_name);
        g_srctypes = configManager.getConfig(INI_VAR_SRCTYPES, g_config_group_name);
        g_ctagscmd = configManager.getConfig(INI_VAR_CTAGSCMD, g_config_group_name);

        logger.log_info("载入工程配置: %s OK", g_configFile.c_str());
        logger.log_info("源码目录清单: %s", g_srcdirs.c_str());
    }
}

// C++11实现一个函数，用来获取字符串里的每个子串，比如 "dir1, dir2,   dir3"，并自动去掉多余空格制表符等空字符
std::vector<std::string> split_string(const std::string &str, char delimiter)
{
    std::vector<std::string> result;
    std::istringstream iss(str);
    std::string item;

    while (std::getline(iss, item, delimiter))
    {
        // 去掉前后空格
        item.erase(0, item.find_first_not_of(" \t"));
        item.erase(item.find_last_not_of(" \t") + 1);
        if (!item.empty())
        {
            result.push_back(item);
        }
    }
    return result;
}

// 判断某目录是否存在，输入参数，目录路径，返回值布尔
bool isExistDirectory(const std::string &dirPath)
{
    struct stat info;
    if (stat(dirPath.c_str(), &info) != 0)
    {
        return false; // stat failed
    }
    return (info.st_mode & S_IFDIR) != 0;
}

void split_file_list(void)
{
    // 命令: rm -rf test_sub_* ; split -d -l 3 test_list.txt test_sub_
    // 说明: 拆分 test_list.txt 文件, 拆分后的每个最多3行, -d表示以index为文件尾缀

    // 命令: rm -rf test_sub_* ; split -d -n l/3 test_list.txt test_sub_
    // 说明: 把总文件拆分成3个子文件并按照行来拆分(-n l/3),并以index作为尾缀(-d)
    std::string cmd_split = "split -d -n l/" + g_jobscnt;
    cmd_split += " " + g_projdir + "/input_filelist_total.txt";
    cmd_split += " " + g_projdir + "/input_filelist_sub_";

    logger.log_debug("%s():cmd_split: %s", __func__, cmd_split.c_str());
    system(cmd_split.c_str());
}


void generate_file_list(void)
{
    // 其实就是执行类似命令: find dir1 dir2 -name "*.c" -o -name "*.h" > vim_project/input_filelist_total.txt

    // g_srcdirs = "dir1, dir2, dir3 " --> "dir1 dir2 dir3"
    std::vector<std::string> dirs = split_string(g_srcdirs, ',');
    std::string find_cmd = "find ";

    // 拼接find目录组合参数
    for (const auto &dir : dirs)
    {
        // 目录不存在则自动忽略
        if (isExistDirectory(dir))
        {
            find_cmd += dir + " ";
        }
    }


    // 拼接find文件类型组合参数
    std::vector<std::string> types = split_string(g_srctypes, ',');
    int count = 0;
    for (const auto &type : types)
    {
        if (count == 0)
        {
            find_cmd += "-name \"" + type + "\"";
        }
        else
        {
            find_cmd += " -o -name \"" + type + "\"";
        }
        count += 1;
    }

    // 拼接find结果重定向命令
    find_cmd += " > " + g_projdir + "/input_filelist_total.txt";

    // 执行find命令
    logger.log_debug("%s():find_cmd: %s", __func__, find_cmd.c_str());
    system(find_cmd.c_str());
}


// 根据传入的线程个数，来判断需要几位后缀编码，1~99=2位数; 100~999=3位数
int jobs_to_digits(int jobs)
{
	int ret = 0;

    // 不可能用这么多线程的设备来看代码
    if (jobs <= 0) ret=-1; // jobs >= 1
    if (jobs > 999999) ret=-2; // jobs <= 999999

	if (ret < 0)
	{
    	logger.log_error("invalid thread_jobs: %d", jobs);
		exit(-1);
	}

    int digits = 2;
    if (jobs >= 100) digits = 3;
    if (jobs >= 1000) digits = 4;
    if (jobs >= 10000) digits = 5;
    if (jobs >= 100000) digits = 6;
    return digits;
}


// 把数字转换成字符串,比如 number=1,zerocnt=4,最终返回 "0001",number=0,zerocnt=4,最终返回"0000"，基于C++11实现
// 将数字转换为指定宽度的带前导零字符串
// number: 要转换的整数
// zerocnt: 最终字符串的总长度
// 示例: number=1, zerocnt=4 → "0001"; number=0, zerocnt=4 → "0000"
std::string number_to_000n(int number, int zerocnt)
{
    // 处理zerocnt为非正数的情况，返回数字本身的字符串形式
    if (zerocnt <= 0)
    {
        return std::to_string(number);
    }

    std::stringstream ss;
    // 设置填充字符为'0'
    ss << std::setfill('0');
    // 设置输出宽度为zerocnt
    ss << std::setw(zerocnt);
    // 插入数字并转换为字符串
    ss << number;

    return ss.str();
}

// 移除字符串中的换行符（'\r'和'\n'），并返回处理后的字符串
// 参数：buf - 输入缓冲区，buflen - 缓冲区长度
std::string text_del_linebreaks(const uint8_t* buf, int buflen) {
    std::string result;
    result.reserve(buflen);  // 预分配内存，提高效率

    // 遍历缓冲区，跳过所有换行符
    for (int i = 0; i < buflen; ++i) {
        uint8_t c = buf[i];
        // 遇到字符串结束符提前退出
        if (c == '\0') {
            break;
        }
        // 跳过'\r'和'\n'，其他字符加入结果
        if (c != '\r' && c != '\n') {
            result += static_cast<char>(c);
        }
    }

    return result;
}



// 从绝对路径中提取文件名（最后一个'/'后的部分）
// 例如："./abc.txt" 返回 "abc.txt"，"/home/user/file" 返回 "file"
// 不含'/'时返回空字符串
std::string path_get_posfname(const std::string& abspath) {
    // 查找最后一个'/'的位置
    size_t last_slash_pos = abspath.find_last_of('/');
    
    // 如果没有找到'/'，返回空字符串
    if (last_slash_pos == std::string::npos) {
        return "";
    }
    
    // 提取并返回最后一个'/'后面的部分
    return abspath.substr(last_slash_pos + 1);
}

int format_filenametags(int group_index) {
    // 计算需要的前置零数量并生成文件名
    int zero_count = jobs_to_digits(std::stoul(g_jobscnt));
    std::string fname_cur = g_projdir + "/output_filenametag_" + number_to_000n(group_index, zero_count);
    std::string fname_tmp = g_projdir + "/output_filenametag_tmp_" + number_to_000n(group_index, zero_count);

    // 使用C++文件流打开文件
    std::ifstream fp_r(fname_cur);
    std::ofstream fp_w(fname_tmp);

    // // 检查文件是否成功打开
    // if (!fp_r.is_open() || !fp_w.is_open()) {
    //     return -1;  // 文件打开失败
    // }

    std::string txtline;
    int lines = 0;

    // 读取文件内容并处理每一行
    while (std::getline(fp_r, txtline)) {
        lines++;
        
        // 移除换行符（使用C++版本的text_del_linebreaks）
        std::string processed_line = text_del_linebreaks(
            reinterpret_cast<const uint8_t*>(txtline.data()), 
            static_cast<int>(txtline.size())
        );
        
        // 提取文件名
        std::string fname = path_get_posfname(processed_line);
        
        // 按照格式写入临时文件
        if (!fname.empty()) {
            fp_w << fname << "\t" << processed_line << "\t1\n";
        }
    }

    // 关闭文件流（超出作用域会自动关闭，但显式关闭更清晰）
    fp_r.close();
    fp_w.close();

    // 构建并执行mv命令
    std::string cmd = "mv " + fname_tmp + " " + fname_cur;
    return std::system(cmd.c_str());
}

void thread_ctags(int group_index)
{
    logger.log_info("running    ---> %s[%d] ...", __func__, group_index);

    // 最终执行命令: "ctags -R --c++-kinds=+px --fields=+iaS --extras=+fq --excmd=p -f output_ctags_01 -L input_filelist_sub_01";

    // "input_filelist_sub_" 生成 "input_filelist_sub_0001" 这样的文件名，其中0001来自group_index的值,利用 std::string 来处理，最终得到 std::string类型的变量
    int zero_count = jobs_to_digits(std::stoul(g_jobscnt));
    std::string input_file = "input_filelist_sub_" + number_to_000n(group_index, zero_count);
    std::string output_file = "output_ctags_" + number_to_000n(group_index, zero_count);

    //std::string ctags_cmd = "ctags --c++-kinds=+px --fields=+iaS --extra=+fq --excmd=p ";
	std::string cmd_ctags = g_ctagscmd;
    cmd_ctags += " -f " + g_projdir + "/" + output_file;
    cmd_ctags += " -L " + g_projdir + "/" + input_file;

    // printf("ctags_cmd: %s\n", ctags_cmd.c_str());
    system((const char *)cmd_ctags.c_str());

    logger.log_info("running(OK)---> %s[%d] complete!", __func__, group_index);
}

void thread_cscope(int group_index)
{
    logger.log_info("running    ---> %s[%d] ...", __func__, group_index);
    int zero_count = jobs_to_digits(std::stoul(g_jobscnt));
    std::string input_file = "input_filelist_sub_" + number_to_000n(group_index, zero_count);
    std::string output_file = "output_cscope_" + number_to_000n(group_index, zero_count);

    // "cscope -Rb -i input_filelist_sub_01 -f output_cscope_01 2>/dev/null";
    std::string cmd_cscope = "cscope -Rb ";
    cmd_cscope += " -i " + g_projdir + "/" + input_file;
    cmd_cscope += " -f " + g_projdir + "/" + output_file;
    // cmd_cscope += " 2>/dev/null";
    system(cmd_cscope.c_str());

    logger.log_info("running(OK)---> %s[%d] complete!", __func__, group_index);
}

void thread_filenametags(int group_index)
{
    logger.log_info("running    ---> %s[%d] ...", __func__, group_index);
    int zero_count = jobs_to_digits(std::stoul(g_jobscnt));
    std::string input_file = "input_filelist_sub_" + number_to_000n(group_index, zero_count);
    std::string output_file = "output_filenametag_" + number_to_000n(group_index, zero_count);

    
    // "cat input_filelist_sub_06 | sort -f >> output_filenametag_06";
    std::string cmd_filenametags = "cat " + g_projdir + "/" + input_file;
    cmd_filenametags += " | sort -f >> " + g_projdir + "/" + output_file;
    //printf("cmd_filenametags: %s\n", cmd_filenametags.c_str());
    system(cmd_filenametags.c_str());

    format_filenametags(group_index);

    logger.log_info("running(OK)---> %s[%d] complete!", __func__, group_index);
}

void combine_to_one_tagsfile(void)
{
    // 把多个tags合并成一个tags
    // dir_proj=`pwd`
    // dir_vim=${dir_proj}/vim_project
    // cat output_ctags_00 > ${dir_proj}/tags
    // cat output_ctags_01 | grep -v "\!_TAG_" >> ${dir_proj}/tags 
    // cat output_ctags_02 | grep -v "\!_TAG_" >> ${dir_proj}/tags 
    // cat output_ctags_03 | grep -v "\!_TAG_" >> ${dir_proj}/tags 

    std::string cmd_combine;


    unsigned long jobs = std::stoul(g_jobscnt);
    int zero_count = jobs_to_digits(jobs);

    std::string output_file = "tags";
    for (unsigned long group_index=0; group_index<jobs; group_index++)
    {
        std::string input_file = "output_ctags_" + number_to_000n(group_index, zero_count);
        if (group_index == 0)
        {
            cmd_combine = "cat " + g_projdir + "/" + input_file + " > " + g_workdir + "/" + output_file;
            system(cmd_combine.c_str());
        }
        else
        {
            cmd_combine = "cat " + g_projdir + "/" + input_file + " | grep -v \"\\!_TAG_\" >> " + g_workdir + "/" + output_file;
            system(cmd_combine.c_str());
        }
    }
}


void combine_to_one_filenametags(void)
{
    std::string cmd_combine;


    // cat 111.txt 222.txt 333.txt | sort -f > total.txt
    unsigned long jobs = std::stoul(g_jobscnt);
    int zero_count = jobs_to_digits(jobs);

    std::string output_file = "output_filenametags";


    // 写入文件头信息
    std::string cmd = "echo '!_TAG_FILE_SORTED  2  /2=foldcase' > ";
    cmd += g_projdir + "/output_filenametags";
    system(cmd.c_str());

    // 把各个子文件直接合并
    for (unsigned long group_index=0; group_index<jobs; group_index++)
    {

        std::string input_file = "output_filenametag_" + number_to_000n(group_index, zero_count);
        cmd_combine = "cat " + g_projdir + "/" + input_file;
        cmd_combine += " >> " + g_projdir + "/" + output_file;
        system(cmd_combine.c_str());

        // 清除序号文件
        // cmd = "rm -rf " + g_projdir + "/" + input_file;
        // system(cmd.c_str());
    }


}




void generate_preload_file(void)
{

    /*
        在 $HOME/.vim/plugin 目录下创建一个名为 vim_projec_reload.vim 文件，内容为:
            command! VimProjectReload call VimProject_Reload()
            function! VimProject_Reload()
                cscope reset
                cscope add vim_project/output_cscope_00
                cscope add vim_project/output_cscope_01
                cscope add vim_project/output_cscope_02
                cscope add vim_project/output_cscope_03
                set tags=
                set tags+=output_ctags_00
                set tags+=output_ctags_01
                set tags+=output_ctags_02
                set tags+=output_ctags_03
                let g:LookupFile_TagExpr='"vim_project/output_filenametags"'
                echo "[vimkernel] vim project tags/cscope database reload complete !!!"
                return 0
            endfunction
        其中 00，01，02，03 是根据 g_jobscnt 的值来确定的。
    */

    // 该代码块是专门用于生成 $HOME/.vim/plugin/vim_project_reload.vim 文件
    {
        std::string dir_home = getenv("HOME");
        std::string dir_vim = dir_home + "/.vim/plugin";
        std::string vim_project_reload_file = dir_vim + "/vim_project_reload.vim";

        unsigned long jobs = std::stoul(g_jobscnt);
        int zero_count = jobs_to_digits(jobs);


        // 创建目录
        mkdir(dir_vim.c_str(), 0755);

        // 创建预加载文件
        std::ofstream ofs(vim_project_reload_file);
        if (!ofs)
        {
            std::cerr << "创建文件失败: " << vim_project_reload_file << std::endl;
            return;
        }

        ofs << "command! VimProjectReload call VimProject_Reload()" << std::endl;
        ofs << "function! VimProject_Reload()" << std::endl;
        ofs << "    cscope reset" << std::endl;//清空
        for (unsigned long i = 0; i < jobs; ++i)
        {
            std::string input_file = "output_cscope_" + number_to_000n(i, zero_count);
            ofs << "    cscope add " << g_projdir << "/" << input_file << std::endl;
        }
        ofs << "    set tags=" << std::endl;// 清空
        // ofs << "    set tags=tags" << std::endl;
        for (unsigned long i = 0; i < jobs; ++i)
        {
            std::string input_file = "output_ctags_" + number_to_000n(i, zero_count);

            // 此处不能使用绝对路径，因为绝对路径会导致SrcExpl窗口找不到sym定义
            ofs << "    set tags+=" << g_projdir << "/" << input_file << std::endl;//patch for SrcExpl
        }
        ofs << "    let g:LookupFile_TagExpr='" << "\"" << g_projdir << "/output_filenametags" << "\"" << "'" << std::endl;
        ofs << "    echo \"[vimkernel] vim project tags/cscope database reload complete !!!\"" << std::endl;
        ofs << "    return 0" << std::endl;
        ofs << "endfunction" << std::endl;

        ofs.close();
    }




    // 在打开vim时通过参数方式传入vim,以便一步载入所有离散的工程文件
    // 在指定该文件作为参数后,vim打开后会自动执行该文件里的相关命令
    // vimcmd: vim_project/vim_project_preload.vim

    // 文件内容样式如下:
    // :cscope reset
    // :cscope add vim_project/output_cscope_00
    // :cscope add vim_project/output_cscope_01
    // :cscope add vim_project/output_cscope_02
    // :cscope add vim_project/output_cscope_03
    // :set tags=
    // :set tags+=output_ctags_00
    // :set tags+=output_ctags_01
    // :set tags+=output_ctags_02
    // :set tags+=output_ctags_03
    // :let g:LookupFile_TagExpr='"vim_project/output_filenametags"'
    // :echo "[vimkernel] vim project tags/cscope database preload complete !!!"
    // 该代码块是专门用于生成 vim_project/vim_project_reload.vim 文件
    {
        unsigned long jobs = std::stoul(g_jobscnt);
        int zero_count = jobs_to_digits(jobs);
        

        std::string vim_project_preload_file = g_projdir + "/vim_project_preload.vim";

        // 创建预加载文件
        std::ofstream ofs(vim_project_preload_file);
        if (!ofs)
        {
            std::cerr << "创建文件失败: " << vim_project_preload_file << std::endl;
            return;
        }

        ofs << ":cscope reset" << std::endl;
        for (unsigned long i = 0; i < jobs; ++i)
        {
            std::string input_file = "output_cscope_" + number_to_000n(i, zero_count);
            ofs << ":cscope add " << g_projdir << "/" << input_file << std::endl;
        }

        // tags=tags;$HOME 中的分号语法表示：从当前文件所在目录开始查找tags文件，若未找到则向上层目录搜索，直到用户主目录（$HOME）
        ofs << ":set tags=" << std::endl;//clean
        // ofs << ":set tags=tags" << std::endl;
        for (unsigned long i = 0; i < jobs; ++i)
        {
            std::string input_file = "output_ctags_" + number_to_000n(i, zero_count);

            // 此处不能使用绝对路径，因为绝对路径会导致SrcExpl窗口找不到sym定义
            ofs << ":set tags+=" << g_projdir << "/" << input_file << std::endl;//patch for SrcExpl
        }
        ofs << ":let g:LookupFile_TagExpr='" << "\"" << g_projdir << "/output_filenametags" << "\"" << "'" << std::endl;
        ofs << ":echo \"[vimkernel] vim project tags/cscope database preload complete !!!\"" << std::endl;

        ofs.close();
    }

}

void generate_project_datas(void)
{
    std::vector<std::thread> threads;
    {
        IniConfigManager configManager;
        configManager.setIniFile(g_configFile);
        std::string strJobs = configManager.getConfig(INI_VAR_JOBSCNT, g_config_group_name);
        long jobs = std::strtol(strJobs.c_str(), nullptr, 10);
        logger.log_info("创建线程数量: %ld", jobs);

        // 创建多个线程
        for (int i = 0; i < jobs; ++i)
        {
            threads.emplace_back(thread_ctags, i);
            threads.emplace_back(thread_cscope, i);
            threads.emplace_back(thread_filenametags, i);

            // delay 50ms
            usleep(50000);
        }
    }

    // 等待所有线程完成
    for (auto &t : threads)
    {
        t.join();
    }


    // 针对tags的特殊处理
	// patch for SrcExpl
    // 此处不能使用绝对路径，因为绝对路径会导致SrcExpl窗口找不到sym定义
    //std::string cmd = "mv " + g_projdir + "/output_ctags_* " + g_workdir + "/";
	//system(cmd.c_str());
    // combine_to_one_tagsfile();

    combine_to_one_filenametags();
}


int main(int argc, char **argv)
{
    // 检查命令行参数: main.elf <workdir>
    if (argc < 2)
    {
        fprintf(stderr, "用法: %s <workdir> [config|clean|clean_objs|clean_cfgs]\n", argv[0]);
        fprintf(stderr, "示例: %s `pwd`             --删除数据库文件并重新生成数据库文件\n", argv[0]);
        fprintf(stderr, "示例: %s `pwd` config      --仅生成/覆写配置文件: vim_project/config.ini\n", argv[0]);
        fprintf(stderr, "示例: %s `pwd` clean       --删除所有工程文件,打包归档会用到此操作\n", argv[0]);
        fprintf(stderr, "示例: %s `pwd` clean_objs  --仅删除工程文件中的数据库文件\n", argv[0]);
        fprintf(stderr, "示例: %s `pwd` clean_cfgs  --仅删除工程文件中的配置文件\n", argv[0]);
        return 1;
    }
    g_arg_workdir = argv[1];
    // 初始化目录参数
    g_workdir = g_arg_workdir;
    g_projdir = g_workdir + "/vim_project";
    {
        struct stat info;
        if ((stat(g_projdir.c_str(), &info) != 0) || (!(info.st_mode & S_IFDIR)))
        {
            std::string cmd = "mkdir -p " + g_projdir;
            system(cmd.c_str());
        }
    }


    // 初始化日志模块
    logger.setLogFile(g_projdir + "/vim_project.log");
    logger.setConsoleLogLevel(LogLevel::INFO);
    logger.setFileLogLevel(LogLevel::WARN);

    if ((argc > 2))
    {
        if (std::string(argv[2]) == "clean")
        {
            // 执行清理操作
            if (cleanProjectFiles() != 0)
            {
                return 1;
            }
            logger.log_debug("工程数据库文件清理完成");
        }

        if (std::string(argv[2]) == "clean_objs")
        {
            // 执行清理操作
            if (cleanProjectFilesOnlyObjs() != 0)
            {
                return 1;
            }
            logger.log_debug("工程对象文件清理完成");
        }

        if (std::string(argv[2]) == "clean_cfgs")
        {
            // 执行清理操作
            if (cleanProjectFilesOnlyCfgs() != 0)
            {
                return 1;
            }
            logger.log_debug("工程配置文件清理完成");
        }

        if (std::string(argv[2]) == "config")
        {
            // 清理原先配置
            cleanProjectFilesOnlyCfgs();

            // 执行配置文件生成操作
            vim_project_config_init();
            logger.log_debug("工程配置文件生成完成");
        }

        return 0;
    }

    // 清零上一次生成的文件
    cleanProjectFilesOnlyObjs();

    // 检查 workdir 指定目录下是否存在 vim_project
    struct stat info;
    if ((stat(g_projdir.c_str(), &info) != 0) || (!(info.st_mode & S_IFDIR)))
    {
        // 创建该目录
        logger.log_info("创建工程目录: %s ...", g_projdir.c_str());
        if (mkdir(g_projdir.c_str(), 0755) == -1)
        {
            logger.log_error("创建工程目录失败: %s %s", g_projdir.c_str(), strerror(errno));
            return 1;
        }
    }

    // 初始化配置文件部分
    vim_project_config_init();

    // 准备开始生成vim所需的工程文件
    struct timeval timestart, timeend;
    gettimeofday(&timestart, NULL);

    // 初始化多线程需要用的信号量
    vim_project_semaphore_init();

    // 生成列表文件
    generate_file_list();

    // 把列表文件根据线程数量等分成小份
    split_file_list();

    // 生成tags文件
    generate_project_datas();

#if 1
    // 把所有子tags合并成总tags
    // combine_to_one_tagsfile();

    // 合并成output_filenametags 
    // combine_to_one_filenametags();

    // 自动创建预加载文件
    generate_preload_file();
#endif //if 0

    gettimeofday(&timeend, NULL);
    long elapsed_us =
        (timeend.tv_sec - timestart.tv_sec) * 1000000 +
        (timeend.tv_usec - timestart.tv_usec);
    long elapsed_ms = elapsed_us / 1000;
    double elapsed_sec = (double)elapsed_ms / 1000;
    logger.log_info("Total elapsed time is %lf sec", elapsed_sec);
    return 0;
}
